///**
// * Copyright 2009-2015 the original author or authors.
// * <p>
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// * <p>
// * http://www.apache.org/licenses/LICENSE-2.0
// * <p>
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//package com.cloud.drore.eboss.locker.interceptor;
//
//
//import com.cloud.drore.eboss.locker.annotation.VersionLocker;
//import com.cloud.drore.eboss.locker.cache.Cache;
//import com.cloud.drore.eboss.locker.cache.LocalVersionLockerCache;
//import com.cloud.drore.eboss.locker.cache.VersionLockerCache;
//import com.cloud.drore.eboss.locker.util.PluginUtil;
//import com.cloud.drore.eboss.login.user.Anon;
//import net.sf.jsqlparser.expression.Expression;
//import net.sf.jsqlparser.expression.LongValue;
//import net.sf.jsqlparser.expression.operators.arithmetic.Addition;
//import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
//import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
//import net.sf.jsqlparser.parser.CCJSqlParserUtil;
//import net.sf.jsqlparser.schema.Column;
//import net.sf.jsqlparser.statement.Statement;
//import net.sf.jsqlparser.statement.update.Update;
//import org.apache.ibatis.binding.BindingException;
//import org.apache.ibatis.binding.MapperMethod;
//import org.apache.ibatis.executor.parameter.ParameterHandler;
//import org.apache.ibatis.executor.statement.StatementHandler;
//import org.apache.ibatis.logging.Log;
//import org.apache.ibatis.logging.LogFactory;
//import org.apache.ibatis.mapping.BoundSql;
//import org.apache.ibatis.mapping.MappedStatement;
//import org.apache.ibatis.mapping.SqlCommandType;
//import org.apache.ibatis.plugin.*;
//import org.apache.ibatis.reflection.MetaObject;
//import org.apache.ibatis.reflection.SystemMetaObject;
//import org.aspectj.lang.reflect.MethodSignature;
//import org.springframework.web.method.HandlerMethod;
//
//import java.lang.reflect.Field;
//import java.lang.reflect.InvocationTargetException;
//import java.lang.reflect.Method;
//import java.sql.Connection;
//import java.util.*;
//import java.util.stream.Collectors;
//
///**
// * <p>MyBatis乐观锁插件<br>
// * <p>MyBatis Optimistic Locker Plugin<br>
// *
// * @author 342252328@qq.com
// * @version 1.0
// * @date 2016-05-27
// * @since JDK1.7
// */
//@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
//public class OptimisticLocker implements Interceptor {
//
//    private static final Log log = LogFactory.getLog(OptimisticLocker.class);
//
//    private static VersionLocker trueLocker;
//    private static VersionLocker falseLocker;
//
//    static {
//        try {
//            trueLocker = OptimisticLocker.class.getDeclaredMethod("versionValueTrue").getAnnotation(VersionLocker.class);
//
//            falseLocker = OptimisticLocker.class.getDeclaredMethod("versionValueFalse").getAnnotation(VersionLocker.class);
//        } catch (NoSuchMethodException | SecurityException e) {
//            throw new RuntimeException("The plugin init faild.", e);
//        }
//    }
//
//    private Properties props = null;
//    private VersionLockerCache versionLockerCache = new LocalVersionLockerCache();
//
//    @VersionLocker(true)
//    private void versionValueTrue() {
//    }
//
//    @VersionLocker(false)
//    private void versionValueFalse() {
//    }
//
//    @Override
//    public Object intercept(Invocation invocation) throws Exception {
//        String versionColumn;
//        String versionField;
//        Method method = invocation.getMethod();
//
//        //判断是不是忽列请求
//
//
//        if (method.getAnnotation(VersionLocker.class) == null || method.getDeclaringClass().getAnnotation(VersionLocker.class) == null) {
//            return invocation.proceed();
//        }
//        if (null == props || props.isEmpty()) {
//            versionColumn = "version1";
//            versionField = "version1";
//        } else {
//            //versionColumn = props.getProperty("versionColumn", "version");
//            //versionField = props.getProperty("versionField", "version");
//            versionColumn = "version1";
//            versionField = "version1";
//        }
//        String interceptMethod = invocation.getMethod().getName();
//        if (!"prepare".equals(interceptMethod)) {
//            return invocation.proceed();
//        }
//        StatementHandler handler = (StatementHandler) PluginUtil.processTarget(invocation.getTarget());
//        MetaObject metaObject = SystemMetaObject.forObject(handler);
//        MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
//        SqlCommandType sqlCmdType = ms.getSqlCommandType();
//        if (sqlCmdType != SqlCommandType.UPDATE) {
//            return invocation.proceed();
//        }
//        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
//        VersionLocker vl = getVersionLocker(ms, boundSql);
//        if (null != vl && !vl.value()) {
//            return invocation.proceed();
//        }
//        Object originalVersion = metaObject.getValue("delegate.boundSql.parameterObject." + versionField);
//        if (originalVersion == null || Long.parseLong(originalVersion.toString()) <= 0) {
//            throw new BindingException("value of version field[" + versionField + "]can not be empty");
//        }
//        String originalSql = boundSql.getSql();
//        if (log.isDebugEnabled()) {
//            log.debug("==> originalSql: " + originalSql);
//        }
//        originalSql = addVersionToSql(originalSql, versionColumn, originalVersion);
//        metaObject.setValue("delegate.boundSql.sql", originalSql);
//        if (log.isDebugEnabled()) {
//            log.debug("==> originalSql after add version: " + originalSql);
//        }
//        return invocation.proceed();
//    }
//
//    private String addVersionToSql(String originalSql, String versionColumnName, Object originalVersion) {
//        try {
//            Statement stmt = CCJSqlParserUtil.parse(originalSql);
//            if (!(stmt instanceof Update)) {
//                return originalSql;
//            }
//            Update update = (Update) stmt;
//            if (!contains(update, versionColumnName)) {
//                buildVersionExpression(update, versionColumnName);
//            }
//            Expression where = update.getWhere();
//            if (where != null) {
//                AndExpression and = new AndExpression(where, buildVersionEquals(versionColumnName, originalVersion));
//                update.setWhere(and);
//            } else {
//                update.setWhere(buildVersionEquals(versionColumnName, originalVersion));
//            }
//            return stmt.toString();
//        } catch (Exception e) {
//            e.printStackTrace();
//            return originalSql;
//        }
//    }
//
//    private boolean contains(Update update, String versionColumnName) {
//        List<Column> columns = update.getColumns();
//        for (Column column : columns) {
//            if (column.getColumnName().equalsIgnoreCase(versionColumnName)) {
//                return true;
//            }
//        }
//        return false;
//    }
//
//    private void buildVersionExpression(Update update, String versionColumnName) {
//
//        List<Column> columns = update.getColumns();
//        Column versionColumn = new Column();
//        versionColumn.setColumnName(versionColumnName);
//        columns.add(versionColumn);
//
//        List<Expression> expressions = update.getExpressions();
//        Addition add = new Addition();
//        add.setLeftExpression(versionColumn);
//        add.setRightExpression(new LongValue(1));
//        expressions.add(add);
//    }
//
//    private Expression buildVersionEquals(String versionColumnName, Object originalVersion) {
//        EqualsTo equal = new EqualsTo();
//        Column column = new Column();
//        column.setColumnName(versionColumnName);
//        equal.setLeftExpression(column);
//        LongValue val = new LongValue(originalVersion.toString());
//        equal.setRightExpression(val);
//        return equal;
//    }
//
//    private VersionLocker getVersionLocker(MappedStatement ms, BoundSql boundSql) {
//
//        Class<?>[] paramCls = null;
//        Object paramObj = boundSql.getParameterObject();
//
//        /******************下面处理参数只能按照下面3个的顺序***********************/
//        /******************Process param must order by below ***********************/
//        // 1、处理@Param标记的参数
//        // 1、Process @Param param
//        if (paramObj instanceof MapperMethod.ParamMap<?>) {
//            MapperMethod.ParamMap<?> mmp = (MapperMethod.ParamMap<?>) paramObj;
//            if (null != mmp && !mmp.isEmpty()) {
//                paramCls = new Class<?>[mmp.size() / 2];
//                int mmpLen = mmp.size() / 2;
//                for (int i = 0; i < mmpLen; i++) {
//                    Object index = mmp.get("param" + (i + 1));
//                    paramCls[i] = index.getClass();
//                    if (List.class.isAssignableFrom(paramCls[i])) {
//                        return falseLocker;
//                    }
//                }
//            }
//
//            // 2、处理Map类型参数
//            // 2、Process Map param
//        } else if (paramObj instanceof Map) {//不支持批量
//            @SuppressWarnings("rawtypes")
//            Map map = (Map) paramObj;
//            if (map.get("list") != null || map.get("array") != null) {
//                return falseLocker;
//            } else {
//                paramCls = new Class<?>[]{Map.class};
//            }
//            // 3、处理POJO实体对象类型的参数
//            // 3、Process POJO entity param
//        } else {
//            paramCls = new Class<?>[]{paramObj.getClass()};
//        }
//
//        Cache.MethodSignature vm = new Cache.MethodSignature(ms.getId(), paramCls);
//        VersionLocker versionLocker = versionLockerCache.getVersionLocker(vm);
//        if (null != versionLocker) {
//            return versionLocker;
//        }
//
//        //   String pname = Arrays.stream(paramCls).map(p -> p.getName()).collect(Collectors.toList()).toString();
//        Class<?> mapper = getMapper(ms);
//        //   Method[] methods = mapper.getMethods();
//
//  /*      for (Method m :methods){
//            Class<?>[] parameterTypes = m.getParameterTypes();
//            String name = Arrays.stream(parameterTypes).map(p -> p.getName()).collect(Collectors.toList()).toString();
//            if (pname.equals(name)){
//                method = m;
//                break;
//            }
//            System.out.println(name);
//        }*/
//        if (mapper != null) {
//            Method m = null;
//            try {
//                m = mapper.getDeclaredMethod(getMapperShortId(ms), paramCls);
//            } catch (SecurityException e) {
//                throw new RuntimeException("The Map type param error." + e, e);
//            } catch (NoSuchMethodException e) {
//                e.printStackTrace();
//            }
//            versionLocker = m.getAnnotation(VersionLocker.class);
//            if (null == versionLocker) {
//                versionLocker = trueLocker;
//            }
//            if (!versionLockerCache.containMethodSignature(vm)) {
//                versionLockerCache.cacheMethod(vm, versionLocker);
//            }
//            return versionLocker;
//        } else {
//            throw new RuntimeException("Config info error, maybe you have not config the Mapper interface");
//        }
//    }
//
//    public static void setInterfaceInfo(Class<?> clazz, String name) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
//        //1.获取所继承的所有接口
//        Class<?>[] interfaces = clazz.getInterfaces();
//        //2.接口中的方法是在子类中进行调用的，所以需要获取子类的实例化对象
//        Object obj = clazz.newInstance();
//        for (int i = 0; i < interfaces.length; i++) {
//            //3.获取接口中所有的方法
//            Method[] methods = interfaces[i].getMethods();
//            for (int j = 0; j < methods.length; j++) {
//                if (!methods[j].getName().equals(name))
//                    return;
//                //4.获取方法中所有的参数
//                Class<?>[] parameterTypes = methods[j].getParameterTypes();
//                //用于保存要传递的数值
//                Object[] values = new Object[parameterTypes.length];
//                String simplename = "";
//                for (int k = 0; k < parameterTypes.length; k++) {
//                    //5.根据types进行传值
//                    values[k] = StringUtils.getValues(parameterTypes[k].getSimpleName());
//                }
//
//                //6.最重要的一步：是子类进行调用方法，所以需要通过子类的class获取方法
//                Method method = clazz.getMethod(methods[j].getName(), parameterTypes);
//                method.invoke(obj, values);
//            }
//            //7获取接口中的相关属性
//            Field[] fields = interfaces[i].getFields();
//            for (int n = 0; n < fields.length; n++) {
//                fields[i].setAccessible(true);
//                System.out.println(fields[n].getName() + "=" + fields[n].get(obj));
//            }
//        }
//    }
//
//
//    private Class<?> getMapper(MappedStatement ms) {
//        String namespace = getMapperNamespace(ms);
//        Collection<Class<?>> mappers = ms.getConfiguration().getMapperRegistry().getMappers();
//        for (Class<?> clazz : mappers) {
//            if (clazz.getName().equals(namespace)) {
//                return clazz;
//            }
//        }
//        return null;
//    }
//
//    private String getMapperNamespace(MappedStatement ms) {
//        String id = ms.getId();
//        int pos = id.lastIndexOf(".");
//        return id.substring(0, pos);
//    }
//
//    private String getMapperShortId(MappedStatement ms) {
//        String id = ms.getId();
//        int pos = id.lastIndexOf(".");
//        return id.substring(pos + 1);
//    }
//
//    @Override
//    public Object plugin(Object target) {
//        if (target instanceof StatementHandler || target instanceof ParameterHandler) {
//            return Plugin.wrap(target, this);
//        } else {
//            return target;
//        }
//    }
//
//    @Override
//    public void setProperties(Properties properties) {
//        if (null != properties && !properties.isEmpty()) props = properties;
//    }
//
//}